package com.ldodds.ot.serializer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import com.hp.hpl.jena.rdf.model.Alt;
import com.hp.hpl.jena.rdf.model.Bag;
import com.hp.hpl.jena.rdf.model.Container;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.RDFList;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.RDFVisitor;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Seq;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.vocabulary.RDF;
import com.ldodds.ot.PropertyHolder;
import com.ldodds.ot.property.PropertyMapper;

public class StatementMapperImpl implements StatementMapper
{
	private PropertyMapper propertyMapper;
	private RDFVisitor visitor;
	
	public StatementMapperImpl() {
		visitor = new StatementVisitor();
	}
	
	public StatementMapperImpl(RDFVisitor visitor, PropertyMapper propertyMapper) {
		this.propertyMapper = propertyMapper;
		this.visitor = visitor;
	}
	
	public void setStatementVisitor(RDFVisitor visitor) {
		this.visitor = visitor;
	}

	public void setPropertyMapper(PropertyMapper propertyMapper) {
		this.propertyMapper = propertyMapper;
	}

	public PropertyHolder populateMapFromCollection(
			PropertyHolder properties, Statement statement) {
		//TODO preconditions?
		RDFNode object = statement.getObject();
		Collection<Object> collection = getCollectionForRDFContainer(object);
		Container container = getContainer(object);
		NodeIterator iter = container.iterator();
		while  ( iter.hasNext() ) {
			RDFNode item = iter.nextNode();
			collection.add( item.visitWith(visitor) );
		}
		String key = propertyMapper.map( statement.getPredicate().getURI() );		
		properties.put(statement.getPredicate().getURI(), key, collection);
		return properties;
	}

	private Collection<Object> getCollectionForRDFContainer(RDFNode object) {
		Collection<Object> collection = null;
		Resource resource = (Resource)object;
		if ( resource.hasProperty(RDF.type, RDF.Seq) ) {
			collection = new ArrayList<Object>();
		}
		else if ( resource.hasProperty(RDF.type, RDF.Alt) ) {
			collection = new ArrayList<Object>();
		}		
		else if ( resource.hasProperty(RDF.type, RDF.Bag) ) {
			collection = new HashSet<Object>();
		}		
		return collection;
	}
	
	private Container getContainer(RDFNode object) {
		Container container = null;
		Resource resource = (Resource)object;
		
		if ( resource.hasProperty(RDF.type, RDF.Seq) ) {
			container = (Seq)resource.as(Seq.class);
		}
		else if ( resource.hasProperty(RDF.type, RDF.Alt) ) {
			container = (Alt)resource.as(Alt.class);
		}		
		else if ( resource.hasProperty(RDF.type, RDF.Bag) ) {
			container = (Bag)resource.as(Bag.class);
		}		
		
		return container;
	}
	
	public PropertyHolder populateMapFromList(
			PropertyHolder properties, Statement statement) {
		//TODO pre-condition?
		RDFNode object = statement.getObject();
		RDFList list = (RDFList)object.as(RDFList.class);
		List<Object> contents = new ArrayList<Object>( list.size() ); 
		for (int i=0; i<list.size(); i++) {
			RDFNode listItem = list.get(i);
			contents.add( listItem.visitWith(visitor) );
		}
		String key = propertyMapper.map( statement.getPredicate().getURI() );		
		properties.put(statement.getPredicate().getURI(), key, contents);
		return properties;
	}

	public PropertyHolder populateMap(PropertyHolder properties,
			Statement statement) {
		String key = propertyMapper.map( statement.getPredicate().getURI() );
		Object value = statement.getObject().visitWith( visitor );
		properties.put(statement.getPredicate().getURI(), key, value);
		return properties;
	}

}
